<!DOCTYPE html>
<!--
@license
Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-->
<html>
<head>
  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
  <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1, user-scalable=yes">
   <script>
    WCT = {waitFor: function (cb) {HTMLImports.whenReady(cb)}}
  </script>
  <script src="./test-flags.js"></script>
  <script src="../node_modules/wct-browser-legacy/browser.js"></script>
  <script src="../node_modules/@webcomponents/webcomponents-platform/webcomponents-platform.js"></script>
  <script src="../node_modules/es6-promise/dist/es6-promise.auto.min.js"></script>
  <script src="../node_modules/@webcomponents/template/template.js"></script>
  <script src="../node_modules/@webcomponents/html-imports/html-imports.min.js"></script>
  <script src="../node_modules/@webcomponents/shadydom/shadydom.min.js"></script>
  <script src="../node_modules/@webcomponents/custom-elements/custom-elements.min.js"></script>
  <script src="../scoping-shim.min.js"></script>
  <script src="../apply-shim.min.js"></script>
  <script src="../custom-style-interface.min.js"></script>
  <script src="module/generated/make-element.js"></script>
  <script src="module/generated/custom-style-element.js"></script>
</head>
<body>

  <custom-style>
    <style>
      x-container, x-sample, x-sample-dynamic, x-container-dynamic {
        display: block;
        padding: 10px;
        margin: 10px;
        border: 1px solid black;
      }

      .target {
        background-color: rgb(0, 255, 0);
      }
    </style>
  </custom-style>

  <template id="x-sample">
    <style>
      .target {
        background-color: rgb(255, 0, 0);
      }
    </style>
    <h2></h2>
    <p>here .target elements are red</p>
    <div class="target">I'm red</div>
    <template id="renderer">
      <div class="target"></div>
    </template>
  </template>

  <template id="x-container">
    <style>
      .target {
        background-color: rgb(123, 123, 123);
      }
    </style>
    <h1>x-container</h1>
    <p>here .target elements are gray</p>
    <div class="target">I'm gray</div>
    <slot></slot>
  </template>

  <h2>body</h2>
  <p>here .target elements are green</p>

  <div class="target">I'm green</div>

  <x-sample id="inBody"></x-sample>

  <x-sample id="inContainer"></x-sample>

  <x-container></x-container>

  <template id="x-dynamic">
    <style>
      span {
        background-color: rgb(123, 123, 123);
      }
    </style>
    <div id="container">
    </div>
  </template>

  <x-dynamic></x-dynamic>

  <template id="out-of-band">
    <style>
      div {
        color: var(--foo);
      }
    </style>
    <div>oob shadowed</div>
  </template>

  <template id="oob-parent">
    <style>
      out-of-band {
        --foo: rgb(0, 0, 255);
      }
    </style>
    <out-of-band></out-of-band>
  </template>

  <template id="oob-other-parent">
    <style>
      out-of-band {
        --foo: rgb(255, 0, 0);
      }
    </style>
  </template>

  <oob-parent></oob-parent>
  <oob-other-parent></oob-other-parent>

  <template id="x-sample-dynamic">
    <style>
      .target {
        background-color: rgb(255, 0, 0);
      }
    </style>
    <h2></h2>
    <p>here .target elements are red</p>
    <div class="target">I'm red</div>
    <template id="renderer">
      <div class="target"></div>
    </template>
  </template>

  <template id="x-container-dynamic">
    <style>
      .target {
        background-color: rgb(123, 123, 123);
      }
    </style>
    <h1>x-container</h1>
    <p>here .target elements are gray</p>
    <div class="target">I'm gray</div>
    <slot></slot>
  </template>

  <x-container-dynamic></x-container-dynamic>

  <template id="css-build" css-build="shady">
    <style>:host{@apply --fake;}</style>
    <div class="style-scope css-build"></div>
  </template>

  <template id="css-build-comment"><!--css-build:shady-->
    <style>:host{@apply --fake;}</style>
    <div class="style-scope css-build-comment"></div>
  </template>

  <script>
    suite('Dynamic Scoping', () => {
      function stamp(parent, host) {
        let template = host.shadowRoot.querySelector('template#renderer')
        let el = template.content.cloneNode(true).querySelector('div.target');
        el.textContent = `stamped by ${host.id}`;
        parent.appendChild(el);
        return el;
      }
      test('DOM is scoped correctly when stamped from an element into document', (done) => {
        let inBody = document.querySelector('x-sample#inBody');
        let inContainer = document.querySelector('x-sample#inContainer');
        makeElement('x-sample', function() {
          this.shadowRoot.querySelector('h2').textContent = `${this.id}`;
        });
        makeElement('x-container');
        setTimeout(() => {
          let body = stamp(document.body, inBody);
          let container = stamp(document.querySelector('x-container').shadowRoot, inContainer);
          requestAnimationFrame(() => {
            assert.equal(getComputedStyle(body).backgroundColor, 'rgb(0, 255, 0)');
            assert.equal(getComputedStyle(container).backgroundColor, 'rgb(123, 123, 123)')
            done();
          });
        }, 300);
      });
      test('DOM is scoped correctly when created dynamically inside a scoped container', (done) => {
        makeElement('x-dynamic', function() {
          let div = this.shadowRoot.querySelector('#container');
          let newDiv = div.cloneNode(true);
          let span = document.createElement('span');
          span.textContent = 'created dynamically';
          newDiv.appendChild(span);
          this.shadowRoot.appendChild(newDiv);
          requestAnimationFrame(() => {
            assert.equal(getComputedStyle(span).backgroundColor, 'rgb(123, 123, 123)');
            done();
          })
        });
      });
      test('moving a custom element between scopes recalculates correctly', function(done) {
        makeElement('out-of-band');
        makeElement('oob-parent');
        makeElement('oob-other-parent');
        let parent = document.querySelector('oob-parent');
        let newParent = document.querySelector('oob-other-parent');
        let oob = parent.shadowRoot.querySelector('out-of-band');
        let shadowDiv = oob.shadowRoot.querySelector('div');
        newParent.shadowRoot.appendChild(oob);
        requestAnimationFrame(() => {
          assert.equal(getComputedStyle(shadowDiv).getPropertyValue('color').trim(), 'rgb(255, 0, 0)');
          done();
        });
      })
      function makeDynamicElement(name, connectedCallback) {
        let template = document.querySelector(`template#${name}`);
        if (template && window.ShadyCSS) {
          window.ShadyCSS.prepareTemplate(template, name);
        }
        window.customElements.define(name, class extends window.HTMLElement {
          constructor() {
            super();
            if (template && !this.shadowRoot) {
              this.attachShadow({mode: 'open'});
              this.shadowRoot.appendChild(document.importNode(template.content, true));
            }
          }
          connectedCallback() {
            window.ShadyCSS && window.ShadyCSS.styleElement(this);
            if (connectedCallback) {
              connectedCallback.call(this);
            }
          }
        });
      }
      test('Nested DOM is scoped correctly when created dynamically inside a dynamic container', function(done) {
        if (!window.customElements.polyfillWrapFlushCallback && window.ShadyDOM && window.ShadyDOM.inUse) {
          /*
           * This test is flaky if running with native custom elements and polyfill shadowdom,
           * as the shadowdom polyfill may render inside of the constructor and create children,
           * which is not allowed in the CE spec.
           */
          this.skip();
        }
        makeDynamicElement('x-container-dynamic');
        makeDynamicElement('x-sample-dynamic');
        const dynamicDiv = document.createElement('div');
        dynamicDiv.classList.add('target');
        dynamicDiv.innerText = 'I was created dynamically';
        const dynamicSample = document.createElement('x-sample-dynamic');
        const dynamicContainer = document.createElement('x-container-dynamic');
        dynamicSample.shadowRoot.appendChild(dynamicDiv);
        dynamicContainer.shadowRoot.appendChild(dynamicSample);
        document.querySelector('x-container-dynamic').shadowRoot.appendChild(dynamicContainer);
        requestAnimationFrame(() => {
          dynamicSample.shadowRoot.querySelectorAll('div.target').forEach((target) =>
            assert.equal(getComputedStyle(target).backgroundColor,'rgb(255, 0, 0)'));
          done();
        });
      });

      test('templates marked with "css-build" will be left alone', function() {
        makeElement('css-build');
        const template = document.querySelector('template#css-build');
        const div = template.content.querySelector('div');
        const divClasses = Array.from(div.classList);
        assert.includeMembers(divClasses, ['style-scope', 'css-build']);
        const style = template.content.querySelector('style');
        if (style) {
          assert.match(style.textContent.trim(), /:host\s*{\s*@apply --fake;\s*}/);
        }
      });

      test('templates with css-build comments will be left alone', function() {
        const template = document.querySelector('template#css-build-comment');
        const buildComment = template.content.firstChild;
        assert.instanceOf(buildComment, Comment, 'first child of template content should be a Comment');
        makeElement('css-build-comment');
        const div = template.content.querySelector('div');
        const divClasses = Array.from(div.classList);
        assert.includeMembers(divClasses, ['style-scope', 'css-build-comment']);
        const style = template.content.querySelector('style');
        if (style) {
          assert.match(style.textContent.trim(), /:host\s*{\s*@apply --fake;\s*}/);
        }
        assert.equal(buildComment.parentNode, null, 'build commment should have been removed');
      });
    });
  </script>
</body>
</html>
